import javax.swing.*;
import java.lang.reflect.Array;

public class Simulator
{
	JTextArea outputWindow;
	int[] register;
	Storage memory;
	Assembled assembled;
	
	public Simulator(JTextArea outputWindow, Assembled assembled)
	{
		this.assembled = assembled;
		this.outputWindow = outputWindow;
		
		memory = new Storage(assembled.getInitialMemory());
		
		register = new int[33];
		register[32] = assembled.getStartPC();
		register[29] = memory.getStackPointer();
	}
	public void execute()
	{
		int[] command;
		int commandType;
		command = assembled.getLine(register[32]-1);
		
		commandType = command[0];
		
		while(commandType != 5 && commandType != -1)
		{
			switch(commandType)
			{
			case 0:
				rcommand(command);
				break;
			case 1:
				icommand(command);
				break;
			case 2:
				branchcommand(command);
				break;
			case 3:
				l_scommand(command);
				break;
			case 4:
				syscall(command);
				break;
			}
			++register[32];
		}
	}
	public void executeLine()
	{
		int[] command;
		int commandType;
		command = assembled.getLine(register[32]-1);
		
		commandType = command[0];
		
		if(commandType != 5 && commandType != -1)
		{
			switch(commandType)
			{
			case 0:
				rcommand(command);
				break;
			case 1:
				icommand(command);
				break;
			case 2:
				branchcommand(command);
				break;
			case 3:
				l_scommand(command);
				break;
			case 4:
				syscall(command);
				break;
			}
			++register[32];
		}
	}
	public void executeLine(int pcOverride)
	{
		int[] command;
		int commandType;
		command = assembled.getLine(pcOverride-1);
		
		commandType = command[0];
		
		if(commandType != 5 && commandType != -1)
		{
			switch(commandType)
			{
			case 0:
				rcommand(command);
				break;
			case 1:
				icommand(command);
				break;
			case 2:
				branchcommand(command);
				break;
			case 3:
				l_scommand(command);
				break;
			case 4:
				syscall(command);
				break;
			}
			register[32] = pcOverride + 1;
		}
	}
	public void syscall(int[] command)
	{
		if(register[2] == 4)//print string
		{
		     outputWindow.append(memory.getString(register[4]));
		}
		else if(register[2] == 1)//print int
		{
		     outputWindow.append("" + register[4]);
		}
		else if(register[2] == 5)//read int
		{
		     //register[2] = Integer.parseInt(inputArea.getText().trim());
		     //register[2] = Integer.parseInt(stdin.readLine().trim());
		     String inputStuff = JOptionPane.showInputDialog("Enter an Integer");
		     register[2] = Integer.parseInt(inputStuff);
		     outputWindow.append("\n");
		}
	}
	//performs all i-type commands
	public void icommand(int[] command)
	{
		int tempone;
		int temptwo;
		
		switch(command[2])
		{
		case 8://addi, add immediate
			register[command[4]] = register[command[3]] + command[5];
			break;
		case 9://addiu, add immediate unsigned
			tempone = register[command[3]];
			temptwo = command[5];
			
			if(tempone < 0)
				tempone *= -1;
			if(temptwo < 0)
				temptwo *= -1;
			
			register[command[4]] = tempone + temptwo;
			break;
			
		case 10://slti, not implemented as it wasnt needed
			if(register[command[3]] < command[4])
				register[command[4]] = 1;
			else
				register[command[4]] = 0;
			break;
			
		case 11://sltiu, not implemented as it wasnt needed
			outputWindow.append("sltiu is not yet implemented\n");
			break;
			
		case 12://andi, not implemented as it wasnt needed
			register[command[4]] = register[command[3]] & command[5];
			break;
			
		case 13://ori, not implemented as it wasnt needed
			register[command[4]] = register[command[3]] | command[5];
			break;
			
		case 14://xori, not implemented as it wasnt needed
			register[command[4]] = register[command[3]] ^ command[5];
			break;
			
		case 15://li "15 0 " + memory(tokenizer.nextToken()) + " " + tokenizer.nextToken();
			register[command[4]] = command[5];
			break;
		}
	}
	public void rcommand(int[] command)
	{
		int tempone;
		int temptwo;
		switch(command[7])
		{
		case 4://sllv currently not implemented, as it was not needed
			register[command[5]] = register[command[4]] << register[command[3]];
			break;
		case 6://srlv currently not implemented, as it was not needed
			register[command[5]] = register[command[4]] >> register[command[3]];
			break;
		case 12://syscall preforms the different system calls
			if(register[2] == 4)//print string
			{
			     outputWindow.append(memory.getString(register[4]));
			}
			else if(register[2] == 1)//print int
			{
			     outputWindow.append("" + register[4]);
			}
			else if(register[2] == 5)//read line
			{
			     //register[2] = Integer.parseInt(inputArea.getText().trim());
			     //register[2] = Integer.parseInt(stdin.readLine().trim());
			     String inputStuff = JOptionPane.showInputDialog("Enter an Integer");
			     register[2] = Integer.parseInt(inputStuff);
			     outputWindow.append("\n");
			}
			break;
		case 32://add adds two registers and stores the result in a register
			register[command[5]] = register[command[3]] + register[command[4]];
			break;
		case 33://addu adds two registers unsigned stores it in a register
			tempone = register[command[3]];
			temptwo = register[command[4]];
			if(tempone < 0)
				tempone *= -1;
			if(temptwo < 0)
				temptwo *= -1;
			register[command[5]] = tempone + temptwo;
			//System.out.println("register " + command[4] + " = " + register[command[4]]);
			break;
		case 34://sub subtracts a register from another, and stores it in a register
			register[command[5]] = register[command[3]] - register[command[4]];
			break;
		case 35://subu same as sub only unsigned
			tempone = register[command[3]];
			temptwo = register[command[4]];
			if(tempone < 0)
				tempone *= -1;
			if(temptwo < 0)
				temptwo *= -1;
			register[command[5]] = tempone - temptwo;
			break;
		case 36:
			register[command[5]] = register[command[3]] & register[command[4]];
			break;
		case 37:
			register[command[5]] = register[command[3]] | register[command[4]];
			break;
		case 38:
			register[command[5]] = register[command[3]] ^ register[command[4]];
			break;
		case 43:
			if(register[command[3]] < register[command[4]])
				register[command[5]] = 1;
			else
				register[command[5]] = 0;
			break;
		}
	}
	public void branchcommand(int[] command)
	{
		int pc = register[32];
		
		switch(command[2])
		{
		case 1://jr performs the jump register command
			pc = register[command[3]];
			break;
			
		case 2://j, performs the jump commmand
			pc = command[3] - 1;
			break;
			
		case 3://jal, performs the jump and link command
			register[31] = pc; //stores the return address in $ra
			pc = command[3] - 1; //creates the new pc
			break;
			
		case 4://beq, performs the branch equal
			if(register[command[3]] == register[command[4]])
				pc = command[5] - 1;
			break;
			
		case 5://bne, performs the branch not equal
			if(register[command[3]] != register[command[4]])
				pc = command[5] - 1;
			break;
			
		case 6://bge, performs the branch greater than or equal
			if(register[command[3]] >= command[4])
				pc = command[5] - 1;
			break;
			
		default://all other branch commands are not currently implemented
			outputWindow.append("misc. branch type not implented, at line " + command[Array.getLength(command) - 1]);
		}
		
		register[32] = pc;
	}
	//performs all the load and store commands
	public void l_scommand(int[] command)
	{
		int temp;
		int memorySize = memory.getStackPointer();
		switch(command[2])
		{
		case 32://la	$a0, str5, load address
			if(command[1] == 3)
				register[command[3]] = command[4];
			else if(command[1] == 4)
				register[command[3]] = command[4] + command[5];
			break;
		case 35://lw	$a0, 0($sp), lowad word
			if(command[1] == 4)
			{
				temp = command[4]/4 + register[command[5]];
				//finds out if the load word command causes an out of bounds error
				if(temp >= memorySize)
				{
					//tells the user that the load word has caused
					//an out of bounds error
					outputWindow.append("out of bounds some how, lw");
					
					//indicates to the user what command caused it
					//well in its decimal form anyway
					for(int i = 1; i < 5; ++i)
						outputWindow.append(command[i] + " ");
					outputWindow.append("\n");
					//tells the user what the pc value was
					//this way they can find the error hopefully
					outputWindow.append("Line number is " + command[Array.getLength(command) - 1]);
				}
				register[command[3]] = memory.getLocation(temp);
			}
			//System.out.println("register " + command[2] + " = " + register[command[2]]);
			break;
		case 43://sw	$a0, 0($sp), store word
			if(command[1] == 4)
			{
				temp = command[4]/4 + register[command[5]];
				
				//finds out if this causes the program to go out of bounds on its memory
				if(temp >= memorySize)
				{
					//tells the user that it has gone out of bounds
					outputWindow.append("out of bounds some how, sw");
					
					//prints out the decimal version of the command that caused it
					for(int i = 1; i < 5; ++i)
						outputWindow.append(command[i] + " ");
					outputWindow.append("\n");
					
					//tells the user the pc value of the line
					outputWindow.append("Line number is " + command[Array.getLength(command) - 1]);
				}
				memory.setLocation(temp, register[command[3]]);
			}
			break;
		}
	}
}